#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <common/macro/build.h>
#include <common/util.h>
#include <query/query.h>
#include <storage/storage.h>
#include <storage/page.h>
#include <storage/blob/page_blob.h>
#include <transaction/transaction.h>

API_LIBLOCAL int query_blobify(query_t *query, char *data, size_t size, page_number_t *page_number_head) {
	char *subset_data;
	page_blob_type_e blob_type;
	page_t *page_current, *page_previous;
	page_number_t page_number_current, page_number_previous;
	page_number_t *page_number_skip;
	size_t offset;
	page_size_t subset_size;

	/* One-way linked list, pages written from the end. */
	page_previous = NULL;
	page_number_previous = 0;
	page_number_current = 0;

	offset = size;
	blob_type = BLOB_TAIL;
	do {
		page_current = page_new(PAGE_BLOB, query->storage->page_size);

		/* Figure out what data. */
		subset_size = page_get_freespace(page_current);
		if (size < subset_size)
			subset_size = size;
		offset -= subset_size;
		size -= subset_size;

		/* Prepare the blob data */
		page_current->blob->type = blob_type;
		page_current->blob->id_insert = query->transaction->id;

		subset_data = util_memdup(&data[offset], subset_size);
		page_current->blob->data = subset_data;
		page_current->blob->size = subset_size;
		if (page_previous != NULL) {
			/* Weird naming (next = previous), but that's because we're
			 * writing in reversed order. */
			page_current->blob->next = page_number_previous;
		}
		page_decrease_freespace(page_current, subset_size);

		/* Find a place to stick it... and stick it there. */
		/* storage_fsm_new_lease(query->storage, &page_number_current); */ /* FIXME HAX */
		if (query->iterator.status == QUERY_ITERATOR_OPEN)
			page_number_skip = &query->iterator.pointer.page_number;
		else
			page_number_skip = NULL;
		storage_fsm_lease_new(query->storage, &page_number_current);
		storage_page_writelock(query->storage, page_number_current);
		storage_page_write(query->storage, page_number_current, page_current);
		storage_fsm_lease_return(query->storage, page_number_current, PAGE_BLOB, page_get_freespace(page_current));

		page_destroy(page_previous);
		free(subset_data);

		page_previous = page_current;
		page_number_previous = page_number_current;
		blob_type = BLOB_DATA;
	} while (size > 0);
	page_destroy(page_previous);

	*page_number_head = page_number_current;
	return 0;
}

API_LIBLOCAL int query_deblobify(query_t *query, char **data, size_t *size, page_number_t page_number_head) {
	page_blob_type_e blob_type;
	page_number_t page_number;
	page_t *page;
	int rv;

	*data = NULL;
	*size = 0;

	page_number = page_number_head;
	do {
		page = NULL;
		rv = storage_page_readlock(query->storage, page_number);
		rv = storage_page_read(query->storage, page_number, &page);
		rv = storage_page_rwunlock(query->storage, page_number);

		(*data) = realloc(*data, (*size) + page->blob->size);
		memcpy(&((*data)[*size]), page->blob->data, page->blob->size);
		(*size) = (*size) + page->blob->size;

		blob_type = page->blob->type;
		page_number = page->blob->next;

		page_destroy(page);

	} while (blob_type != BLOB_SINGLE && blob_type != BLOB_TAIL);

	return 0;
}

